package cn.shop.front.config;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.shop.utils.ApiCode;
import cn.shop.utils.ApiResult;
import cn.shop.utils.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * 全局异常处理
 *
 * @author jzw
 * @date 2019-08-03 13:00
 */
@Slf4j
@RestControllerAdvice
public class GlobalEctHandler {

	private void logRequest(HttpServletRequest req, Exception e) {
		if (req != null) {
			log.error("请求方法:{},请求地址:{},程序异常:{}", req.getMethod(), req.getRequestURL(), e);
		}
		else {
			log.error("程序异常:", e);
		}
	}

	@ExceptionHandler(value = Exception.class)
	ApiResult<Map<String, Object>> handleException(Exception e, HttpServletRequest req) {
		if (e instanceof NotLoginException) {
			return ApiResult.fail(ApiCode.NO_LOGIN);
		}
		else if (e instanceof NotRoleException) {
			return ApiResult.fail(ApiCode.NOT_ROLE);
		}
		else if (e instanceof NotPermissionException) {
			return ApiResult.fail(ApiCode.NOT_PERMISSION);
		}
		else {   // 普通异常, 输出：500 + 异常信息
			logRequest(req, e);
			return ApiResult.fail(ApiCode.UNKNOWN_ERROR, "服务器异常");
		}
	}

	@ExceptionHandler(value = MaxUploadSizeExceededException.class)
	ApiResult<Map<String, Object>> uploadException(MaxUploadSizeExceededException e, HttpServletRequest req) {
		logRequest(req, e);
		String message = e.getMessage();
		log.error("操作失败:{}", message);
		return ApiResult.fail("上传文件不能超过10M");
	}

	@ExceptionHandler(value = ServiceException.class)
	ApiResult<Map<String, Object>> serviceException(ServiceException e, HttpServletRequest req) {
		logRequest(req, e);
		ApiCode code = e.getCode();
		String message = e.getMessage();
		log.error("操作失败:{}", message);
		if (null != code) {
			return ApiResult.fail(code, message);
		}
		return ApiResult.fail(message);
	}

	@ExceptionHandler(value = IllegalArgumentException.class)
	ApiResult<Map<String, Object>> handleIllegalArgumentException(Exception e, HttpServletRequest req) {
		logRequest(req, e);
		return ApiResult.fail(e.getMessage());
	}


	/**
	 * 缺少参数异常 getParameterName() 或 @RequestParam
	 */
	@ExceptionHandler({MissingServletRequestParameterException.class})
	public ApiResult<Map<String, Object>> requestMissingServletRequest(MissingServletRequestParameterException e, HttpServletRequest req) {
		logRequest(req, e);
		return ApiResult.fail(ApiCode.PARAMETER_ERROR, "缺少必要参数,参数名称为" + e.getParameterName());
	}

	/**
	 * controller方法参数上直接添加校验的异常控制
	 */
	@ExceptionHandler(value = ConstraintViolationException.class)
	public ApiResult<Map<String, Object>> constraintViolationException(ConstraintViolationException e, HttpServletRequest req) {
		logRequest(req, e);
		Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
		ConstraintViolation<?> violation = violations.iterator().next();
		String message = violation.getMessage();
		return ApiResult.fail(ApiCode.PARAMETER_ERROR, message);
	}

	/**
	 * get请求参数校验抛出的异常
	 */
	@ExceptionHandler(BindException.class)
	public ApiResult<Map<String, Object>> bindExceptionHandler(BindException e, HttpServletRequest req) {
		logRequest(req, e);
		//获取异常中随机一个异常信息
		String defaultMessage = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
		return ApiResult.fail(ApiCode.PARAMETER_ERROR, ApiCode.PARAMETER_ERROR.description());
	}

	/**
	 * 处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常。
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public ApiResult<Map<String, Object>> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e, HttpServletRequest req) {
		//logRequest(req, e);
		String message = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
		return ApiResult.fail(ApiCode.PARAMETER_ERROR, message);
	}

	/**
	 * 参数类型转换错误
	 */
	@ExceptionHandler(HttpMessageConversionException.class)
	public ApiResult<Map<String, Object>> parameterTypeException(HttpMessageConversionException e, HttpServletRequest req) {
		logRequest(req, e);
		return ApiResult.fail(ApiCode.PARAMETER_ERROR);

	}

}
